﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Algobox.Toolbox.Collections;
using Algobox.Objects.Finance.Assets.Stock;
using System.ComponentModel;
using Algobox.Feeds.Finance.MarketData;
using Algobox.Toolbox.Collections.TimeSeries;
using System.Threading;

namespace Algobox.Structure.Finance.Strategies.Moss
{
    public class MossStock : Strategy, INotifyPropertyChanged, IMarketUpdateLast, IMossUnitCallback, LimitSnatcher.ISnatched
    {
        #region Constructors

        public MossStock(MossManagement manager,
            Algobox.Feeds.Finance.Trading.ITradingFeedAdaptor tradingFeed,
            StockSecurity security,
            Guid databaseId,
            string account,
            int unitValue,
            int rsiTop,
            int rsiBottom,
            int rsiRetrace,
            int bollyPeriod,
            double bollyStdDevs)
            :base(security)
        {
            _isEnabled = false;
            _manager = manager;
            _tradingFeed = tradingFeed;
            DatabaseId = databaseId;
            Account = account;
            _security = security;            
            _unitValue = unitValue;
            _rsiTop = rsiTop;
            _rsiBottom = rsiBottom;
            _rsiRetrace = rsiRetrace;
            _bollyPeriod = bollyPeriod;
            _bollyStdDevs = bollyStdDevs;

            _entryOrder = new LimitSnatcher.LimitSnatchOrder(tradingFeed, this);

            _settings = String.Format("{0}:{1}:{2}:{3}:{4}", _rsiTop, _rsiBottom, _rsiRetrace, _bollyPeriod, _bollyStdDevs);

            _listing = security.Listings[Algobox.Objects.Finance.Exchanges.ExchangeMIC.BATE];

            Phase = Phases.Initialised;
            _barIndex = -1;

            if (security.Currency == Objects.Finance.Currencies.Currency.GBX)
                _divisor = 100;
            else
                _divisor = 1;

            Log.Info("{0}|{1}|MossStock Creating Moss strategy with a unit value of {2} with Account({3}) DatabaseId({4})", _strategyIdString, _settings, unitValue, account, databaseId);
        }

        #endregion
        //////////
        #region Member Variables

        /// <summary>
        /// Bars range to begin at 8am
        /// </summary>
        const double BARS_START = 8;

        /// <summary>
        /// Bar range to end at 4:30pm
        /// </summary>
        const double BARS_END = 16.5;

        /// <summary>
        /// Default bar size of 1 minute
        /// </summary>
        const double BAR_SIZE = 1;

        /// <summary>
        /// Mid point of the RSI... 50!
        /// </summary>
        const double RSI_MID = 50;

        /// <summary>
        /// RSI Resistance cutoff ... 60
        /// </summary>
        const double RSI_RESISTANCE_CUTOFF = 60;

        /// <summary>
        /// RSI support cuttof ... 40
        /// </summary>
        const double RSI_SUPPORT_CUTOFF = 40;
   
        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

        public event PropertyChangedEventHandler PropertyChanged;

        private MossManagement _manager;
        private Algobox.Feeds.Finance.Trading.ITradingFeedAdaptor _tradingFeed;
          private StockListing _listing;
        private Phases _phase;
        private OHLCMinuteBars _bars;
        private object _lock = new object();

        private string _settings;
        private bool _isEnabled;
        private string _account;
        private int _unitValue;
        private int _rsiTop;
        private int _rsiBottom;
        private int _rsiRetrace;
        private int _bollyPeriod;
        private double _bollyStdDevs;
        private int _divisor;

        private double _last;
        private double _rsi;
        private double _bollyHigh;
        private double _bollyLow;
        private double _movingAverage;
        private double _mid;
        private int _barIndex;
        private int _barsInitCount;

        private double _phase1RSI;
        private double _phase1Price;
        private double _phase2RSI;
        private double _phase3RSI;
        private double _phase3Price;
        private double _phase4RSI;
        private double _triggerBarCount;
        
        private MossUnit _triggeredUnit1;
        private MossUnit _triggeredUnit2;
        private LimitSnatcher.LimitSnatchOrder _entryOrder;

        #endregion
        //////////
        #region Properties

        public Guid DatabaseId { get; private set; }
        public string Name { get { return ToString(); } }

        public bool IsEnabled 
        { 
            get { return _isEnabled; } 
            set 
            { 
                lock (_lock)  
                    _isEnabled = value;

                if (value && _phase == Phases.FillFailure)
                {
                    Log.Info("{0}|{1}|IsEnabled User has re-enabled after a fill failure", _security, _settings);
                    _phase = Phases.BuildingData;
                }

                Log.Info("{0}|{1}|IsEnabled {2} ", _security, _settings, value ? "enabled" : "disabled"); 
                NotifyPropertyChanged("IsEnabled"); 
            } 
        }

        public string Account
        {
            get { return _account; }
            set
            {
                if(Log.IsInfoEnabled)
                    Log.Info("{0}|{1}|Account changing from {2} to {3}", _security, _settings, _account, value);

                lock (_lock)
                    _account = value;

                NotifyPropertyChanged("Account");
            }
        }

        public int UnitValue { get { return _unitValue; } set { Log.Info("{0}|{1}|UnitValue changing from {2} to {3}", _strategyIdString, _settings, _unitValue, value); _unitValue = value; NotifyPropertyChanged("UnitValue"); } }
        public Phases Phase { get { return _phase; } private set { Log.Info("{0}|{1}|Phase changing from {2} to {3}", _strategyIdString, _settings, _phase, value); _phase = value; NotifyPropertyChanged("Phase"); } }

        public int RSITop { get { return _rsiTop; } set { if (Log.IsDebugEnabled) Log.Debug("{0}|{1}|RSITop changing from {2} to {3}", _strategyIdString, _settings, _rsiTop, value); _rsiTop = value; _settings = ToString(); NotifyPropertyChanged("RSITop"); NotifyPropertyChanged("Security"); } }
        public int RSIBottom { get { return _rsiBottom; } set { if (Log.IsDebugEnabled) Log.Debug("{0}|{1}|RSIBottom changing from {2} to {3}", _strategyIdString, _settings, _rsiBottom, value); _rsiBottom = value; _settings = ToString(); NotifyPropertyChanged("RSIBottom"); NotifyPropertyChanged("Security"); } }
        public int RSIRetrace { get { return _rsiRetrace; } set { if (Log.IsDebugEnabled) Log.Debug("{0}|{1}|RSIRetrace changing from {2} to {3}", _strategyIdString, _settings, _rsiRetrace, value); _rsiRetrace = value; _settings = ToString(); NotifyPropertyChanged("RSIRetrace"); NotifyPropertyChanged("Security"); } }
        public int BollyPeriod { get { return _bollyPeriod; } set { if (Log.IsDebugEnabled) Log.Debug("{0}|{1}|BollyPeriod changing from {2} to {3}", _strategyIdString, _settings, _bollyPeriod, value); _bollyPeriod = value; _settings = ToString(); NotifyPropertyChanged("BollyPeriod"); NotifyPropertyChanged("Security"); } }
        public double BollySTDEV { get { return _bollyStdDevs; } set { if (Log.IsDebugEnabled) Log.Debug("{0}|{1}|BollySTDEV changing from {2} to {3}", _strategyIdString, _settings, _bollyStdDevs, value); _bollyStdDevs = value; _settings = ToString(); NotifyPropertyChanged("BollySTDEV"); NotifyPropertyChanged("Security"); } }

        public double Last { get { return _last; } private set { _last = value; NotifyPropertyChanged("Last"); } }
        public double BollyHigh { get { return _bollyHigh; } private set { _bollyHigh = value; NotifyPropertyChanged("BollyHigh"); } }
        public double BollyLow { get { return _bollyLow; } private set { _bollyLow = value; NotifyPropertyChanged("BollyLow"); } }
        public double Average { get { return _movingAverage; } private set { _movingAverage = value; NotifyPropertyChanged("Average"); } }
        public double RSI
        { 
            get { return _rsi; } private 
            set
            {
                if (Log.IsTraceEnabled) Log.Trace(_strategyIdString + "|{0}| {1}:RSI:Last |{2}|" + _last, _settings, _security, value);
                _rsi = value; 
                NotifyPropertyChanged("RSI"); 
            } 
        }



        #endregion
        //////////
        #region Functions

        public override string ToString()
        {
            return String.Format("{0}:{1}:{2}:{3}:{4}:{5}", _security.ToString(), _rsiTop, _rsiBottom, _rsiRetrace, _bollyPeriod, _bollyStdDevs);
        }

        private void NotifyPropertyChanged(string propertyName)
        {            
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void OnPriceLast(IStock stock, Objects.Finance.Prices.PriceVolume last)
        {
            if (last.TimeStamp.TimeOfDay < MossManagement.TIME_BARS_START)
                return;

            if (Monitor.TryEnter(_lock, 10))
            {
                try
                {
                    //if(last.TimeStamp.TimeOfDay >= new TimeSpan(9,40,0))
                    //{
                    //    Log.Info("{0}|{1}|OnPriceLast Initialising price bars to {2} minutes between {3} and {4}", _security, _id, _bars.BarSize, _bars.StartTime, _bars.EndTime);
                    //}

                    int barIndex;
                    if (_bars == null)
                    {
                        // itinialise bars to 8am fixed range
                        //_bars = new OHLCMinuteBars(DateTime.Now.Date.AddHours(BARS_START), DateTime.Now.Date.AddHours(BARS_END), TimeSpan.FromMinutes(BAR_SIZE));

                        // initialiase bars to begin when the first datapoint arrives
                        _bars = new OHLCMinuteBars(last.TimeStamp.Date.AddHours(BARS_START), last.TimeStamp.Date.AddHours(BARS_END), TimeSpan.FromMinutes(BAR_SIZE));

                        if (Log.IsInfoEnabled)
                        {
                            Log.Info("{0}|{1}|OnPriceLast Initialising price bars to {2} minutes between {3} and {4}", _strategyIdString, _settings, _bars.BarSize, _bars.StartTime, _bars.EndTime);
                        }

                        _bars.TryAdd(last.TimeStamp, last.Price, last.Volume, out barIndex);

                        // set the barIndex member to the starting point of the first datapoint
                        // this is necessary only if _bars is initialised with a fixed time point such as 8am
                        // otherwise if initialised to an arbitrary time point such as the first price timestamp then
                        // barIndex will always start at 0
                        _barIndex = barIndex;
                    }
                    else
                    {
                        if(!_bars.TryAdd(last.TimeStamp, last.Price, last.Volume, out barIndex))
                        {
                            if (Phase != Phases.DoneForTheDay)
                                Phase = Phases.DoneForTheDay;

                            return;
                        }
                    }

                    double bollyTop, bollyMiddle, bollyBottom;

                    switch (_phase)
                    {
                        case Phases.Initialised:
                            Phase = Phases.BuildingData;
                            // do something with the data
                            break;

                        case Phases.FillFailure:
                        case Phases.BuildingData:
                            // check if we have enough data built
                            // all bars need to be full of data before starting so must be > than num entry bars required
                            if (barIndex != _barIndex)
                            {
                                int numBars = barIndex - _barIndex;
                                if (numBars + _barsInitCount > _bollyPeriod)
                                {
                                    _barsInitCount = _bollyPeriod + 1;
                                }
                                else
                                {
                                    _barsInitCount += numBars;
                                }
                                _barIndex = barIndex;
                            }
                            if (_barsInitCount > _bollyPeriod)
                            {
                                Phase = Phases.Analysing;
                                _mid = _bars.Mid;
                            }
                            break;

                        case Phases.Analysing:
                            if (barIndex != _barIndex)
                            {
                                _barIndex = barIndex;
                                _triggerBarCount = 0;
                                // get current RSI
                                RSI = _bars.GetRSI(barIndex - 1, _bollyPeriod, OHLCMinuteBars.Data.Close);
                            }
                            
                            _bars.GetBollingerBands(barIndex, _bollyPeriod, _bollyStdDevs, out bollyTop, out bollyMiddle, out bollyBottom, OHLCMinuteBars.Data.Mid, OHLCMinuteBars.MAType.WMA);
                            BollyHigh = bollyTop;
                            BollyLow = bollyBottom;
                            Average = bollyMiddle;

                            if (_rsi >= _rsiTop)
                            {                                
                                CheckTop(last.Price);
                            }
                            else if (_rsi <= _rsiBottom)
                            {
                                CheckBottom(last.Price);
                            }
                            break;

                        case Phases.Phase1:
                        case Phases.Phase2:
                        case Phases.Phase3:
                        case Phases.Phase4:
                            if (barIndex != _barIndex)
                            {
                                _barIndex = barIndex;
                                _triggerBarCount++;
                                                          
                                // get current RSI
                                RSI = _bars.GetRSI(barIndex, _bollyPeriod, OHLCMinuteBars.Data.Close);

                                if (_rsi >= RSI_RESISTANCE_CUTOFF)
                                {
                                    CheckTop(last.Price);
                                }
                                else if (_rsi <= RSI_SUPPORT_CUTOFF)
                                {
                                    CheckBottom(last.Price);
                                }
                                else
                                {
                                    Phase = Phases.Analysing;
                                }                                
                            }
                            break;

                        case Phases.Triggered:
                            if (barIndex != _barIndex || last.Price != _last)
                            {
                                _barIndex = barIndex;

                                _bars.GetBollingerBands(barIndex, _bollyPeriod, _bollyStdDevs, out bollyTop, out bollyMiddle, out bollyBottom, OHLCMinuteBars.Data.Mid, OHLCMinuteBars.MAType.WMA);
                                BollyHigh = bollyTop;
                                BollyLow = bollyBottom;
                                Average = bollyMiddle;

                                // update unit 1
                                if (_triggeredUnit1 != null)
                                {
                                    _triggeredUnit1.ExitTarget = bollyMiddle;
                                    _triggeredUnit1.OnBestPrice(last.Price, last.TimeStamp);
                                }
                                // update unit 2
                                if(_triggeredUnit2 != null)
                                {
                                    if (_triggeredUnit2.IsBuy)
                                    {
                                        _triggeredUnit2.ExitTarget = bollyTop;
                                    }
                                    else
                                    {
                                        _triggeredUnit2.ExitTarget = bollyBottom;
                                    }
                                    _triggeredUnit2.OnBestPrice(last.Price, last.TimeStamp);
                                }
                            }
                            break;

                        default:
                            throw new NotImplementedException();

                    } // switch

                    // update last price
                    Last = last.Price;
                }
                finally
                {
                    Monitor.Exit(_lock);
                }
            }
            else if (Log.IsTraceEnabled)
            {
                Log.Trace("{0}|{1}|OnPriceLast couldn't enter lock after 10 milliseconds", _strategyIdString, _settings);
            }
        }

        private bool CheckBottom(double price)
        {
            switch (_phase)
            {
                default:
                    _phase1RSI = _rsi;
                    _phase1Price = price;
                    Phase = Phases.Phase1;
                    return false;

                // first dip
                case Phases.Phase1:
                    if (_rsi > _rsiBottom)
                    {
                        Phase = Phases.Analysing;
                        return false;
                    }
                    else if (_rsi <= _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        return false;
                    }
                    // therefore _rsi > _phase1RSI and started first rise
                    _phase2RSI = _rsi;
                    Phase = Phases.Phase2;
                    return false;

                // first rise
                case Phases.Phase2:
                    if (_rsi >= _phase2RSI)
                    {
                        _phase2RSI = _rsi;
                        return false;
                    }
                    else if (_rsi < _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }
                    // therefore _rsi < _phase2RSI
                    // if we have a big enough retrace then enter second dip
                    if (_phase2RSI - _phase1RSI >= _rsiRetrace)
                    {
                        _phase3RSI = _rsi;
                        _phase3Price = price;
                        Phase = Phases.Phase3;
                        return false;
                    }
                    _phase1RSI = _rsi;
                    _phase1Price = price;
                    Phase = Phases.Phase1;
                    return false;

                // second dip
                case Phases.Phase3:
                    // dipped below phase1
                    if (_rsi < _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }
                    // still dipping
                    else if (_rsi <= _phase3RSI && _rsi >= _phase1RSI)
                    {
                        _phase3RSI = _rsi;
                        _phase3Price = price;
                        return false;
                    }
                    // troughed and retraced
                    else if (_rsi - _phase3RSI >= _rsiRetrace / 2)
                    {
                        _phase4RSI = _rsi;
                        if (_phase3Price < _phase1Price)
                        {
                            EnterLong(price);
                            Phase = Phases.Triggered;
                            return true;
                        }
                        else
                        {
                            Phase = Phases.Phase4;
                            return false;
                        }
                    }
                    // _rsi troughed and started second rise
                    if (_phase3RSI > _rsiBottom)
                    {
                        // _phase3RSI never made it below the _rsiBottom
                        Phase = Phases.Analysing;
                        return false;
                    }
                    // troughed and started second rise
                    _phase4RSI = _rsi;
                    Phase = Phases.Phase4;
                    return false;

                case Phases.Phase4:
                    // dipped below _phase1 start again
                    if (_rsi < _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }
                    // dipped below _phase4 so starting again
                    else if (_rsi < _phase4RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }

                    _phase4RSI = _rsi;
                    // have we retraced enough to be triggered
                    if (_phase4RSI - _phase3RSI >= _rsiRetrace / 2)
                    {
                        if (_phase3Price < _phase1Price)
                        {
                            EnterLong(price);
                            Phase = Phases.Triggered;
                            return true;
                        }
                        else
                        {
                            Phase = Phases.Phase4;
                            return false;
                        }
                    }
                    return false;
            }
        }


        private bool CheckTop(double price)
        {
            switch(_phase)
            {
                default:
                    _phase1RSI = _rsi;
                    _phase1Price = price;
                    Phase = Phases.Phase1;
                    return false;

                // first rise
                case Phases.Phase1:
                    if (_rsi < _rsiTop)
                    {
                        Phase = Phases.Analysing;
                        return false;
                    }
                    else if (_rsi >= _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        return false;
                    }
                    // therefore _rsi < _phase1RSI
                    _phase2RSI = _rsi;
                    Phase = Phases.Phase2;
                    return false;

                // first dip
                case Phases.Phase2:
                    if (_rsi <= _phase2RSI)
                    {
                        _phase2RSI = _rsi;                        
                        return false;
                    }
                    else if (_rsi > _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }
                    // therefore _rsi > _phase2RSI
                    // if we have a big enough retrace then move up phases
                    if (_phase1RSI - _phase2RSI >= _rsiRetrace)
                    {
                        _phase3RSI = _rsi;
                        _phase3Price = price;
                        Phase = Phases.Phase3;
                        return false;
                    }
                    _phase1RSI = _rsi;
                    _phase1Price = price;
                    Phase = Phases.Phase1;
                    return false;

                // second rise
                case Phases.Phase3:
                    // risen above phase1
                    if (_rsi > _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }
                    // still rising
                    else if (_rsi >= _phase3RSI && _rsi <= _phase1RSI)
                    {
                        _phase3RSI = _rsi;
                        _phase3Price = price;
                        return false;
                    }
                    // peaked and dipped below phase2 so triggered skipping phase4
                    else if (_phase3RSI - _rsi >= _rsiRetrace / 2)
                    {
                        _phase4RSI = _rsi;
                        if (_phase3Price > _phase1Price)
                        {
                            EnterShort(price);
                            Phase = Phases.Triggered;
                            return true;
                        }
                        else
                        {
                            Phase = Phases.Phase4;
                            return false;
                        }
                    }
                    // _rsi peaked and started second dip
                    if (_phase3RSI < _rsiTop)
                    {
                        // _phase3RSI never made it to peak above _rsiTop
                        Phase = Phases.Analysing;
                        return false;                   
                    }
                    // peaked and started second dip
                    _phase4RSI = _rsi;
                    Phase = Phases.Phase4;   
                    return false;

                case Phases.Phase4:
                    // risen above _phase1
                    if (_rsi > _phase1RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }
                    // risen above _phase4 so starting again
                    else if (_rsi > _phase4RSI)
                    {
                        _phase1RSI = _rsi;
                        _phase1Price = price;
                        Phase = Phases.Phase1;
                        return false;
                    }

                    _phase4RSI = _rsi;
                    // have we retraced enough to be triggered
                    if (_phase3RSI - _phase4RSI >= _rsiRetrace / 2)
                    {
                        if (_phase3Price > _phase1Price)
                        {
                            EnterShort(price);
                            Phase = Phases.Triggered;
                            return true;
                        }
                        else
                        {
                            Phase = Phases.Phase4;
                            return false;
                        }
                    }
                    return false;
            }
        }
        
        private void EnterLong(double price)
        {
            if (_isEnabled && Phase != Phases.Triggered)
            {
                uint quantity = (uint)(_unitValue / (_phase3Price / _divisor));
                string message;

                if (_entryOrder.Snatch(_account, _listing, true, _listing.TickSize.Round(price), quantity, Objects.Finance.Exchanges.Destinations.BATE_CYCLE, out message))
                {
                    Phase = Phases.Triggered;
                    Log.Info("{0}|{1}|EnterLong Snatching {2}@{3} Phase1RSI({4}) Phase2RSI({5}) Phase3RSI({6}) Phase4RSI({7}) Phase1Price({8}) Phase3Price({9}) ", _strategyIdString, _settings, quantity, _last, _phase1RSI, _phase2RSI, _phase3RSI, _phase4RSI, _phase1Price, _phase3Price);
                }
                else
                {
                    Log.Info("{0}|{1}|EnterLong Failed to snatch entery order for {2}@{3} Message({10}) Phase1RSI({4}) Phase2RSI({5})  Phase3RSI({6}) Phase4RSI({7}) Phase1Price({8}) Phase3Price({9}) ", _strategyIdString, _settings, quantity, _last, _phase1RSI, _phase2RSI, _phase3RSI, _phase4RSI, _phase1Price, _phase3Price, message);
                }
            }
            else
            {
                Log.Debug("{0}|{1}|EnterLong Disabled so filtered long entry at {2}", _strategyIdString, _settings, _last);
            }
        }

        private void EnterShort(double price)
        {
            if (_isEnabled && Phase != Phases.Triggered)
            {
                uint quantity = (uint)(_unitValue / (_phase3Price / _divisor));                
                string message;

                if (_entryOrder.Snatch(_account, _listing, false, _listing.TickSize.Round(price), quantity, Objects.Finance.Exchanges.Destinations.BATE_CYCLE, out message))
                {
                    Phase = Phases.Triggered;
                    Log.Info("{0}|{1}|EnterShort Snatching {2}@{3} Phase1RSI({4}) Phase2RSI({5}) Phase3RSI({6}) Phase4RSI({7}) Phase1Price({8}) Phase3Price({9}) ", _strategyIdString, _settings, quantity, _last, _phase1RSI, _phase2RSI, _phase3RSI, _phase4RSI, _phase1Price, _phase3Price);
                }
                else
                {
                    Log.Info("{0}|{1}|EnterShort Failed to snatch entery order for {2}@{3} Message({10}) Phase1RSI({4}) Phase2RSI({5})  Phase3RSI({6}) Phase4RSI({7}) Phase1Price({8}) Phase3Price({9}) ", _strategyIdString, _settings, quantity, _last, _phase1RSI, _phase2RSI, _phase3RSI, _phase4RSI, _phase1Price, _phase3Price, message);
                }
            }
            else
            {
                Log.Debug("{0}|{1}|EnterShort Disabled so filtered long entry at {2}", _strategyIdString, _settings, _last);
            }
        }

        public void OnMossUnitComplete(MossUnit unit)
        {
            lock (_lock)
            {
                if (unit == _triggeredUnit1)
                {
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|{1}|OnMossUnitComplete recieved completed unit 1 with a profit of {2}", _strategyIdString, _settings, unit.Profit);

                    _triggeredUnit1 = null;
                }
                else if (unit == _triggeredUnit2)
                {
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|{1}|OnMossUnitComplete recieved completed unit 2 with a profit of {2}", _strategyIdString, _settings, unit.Profit);

                    _triggeredUnit2 = null;
                }

                if (_triggeredUnit1 == null && _triggeredUnit2 == null)
                {
                    Phase = Phases.BuildingData;
                }
            }
        }

        public void OnMossUnitFailed(MossUnit unit)
        {
            lock (_lock)
            {

                if (unit == _triggeredUnit1)
                {
                    if (Log.IsErrorEnabled)
                        Log.Error("{0}|{1}|OnMossUnitFailed has failed to fill unit 1 so is disabling this strategy", _strategyIdString, _settings);

                    _triggeredUnit1 = null;
                }
                else if (unit == _triggeredUnit2)
                {
                    if (Log.IsErrorEnabled)
                        Log.Error("{0}|{1}|OnMossUnitFailed has failed to fill unit 2 so is disabling this strategy", _strategyIdString, _settings);

                    _triggeredUnit2 = null;
                }
                
                IsEnabled = false;

                // get the channel that includes the current bar                
                _barsInitCount -= 1;

                Phase = Phases.FillFailure;
            }
        }


        #region ISnatched Members

        public string Id
        {
            get { return base.Id; }
        }

        public void OnSnatchComplete(LimitSnatcher.LimitSnatchOrder snatcher, Objects.Finance.Orders.Order order)
        {
            System.Diagnostics.Debug.Assert(snatcher == _entryOrder && _triggeredUnit1 == null && _triggeredUnit2 == null);

            uint quantity = order.QuantityFilled / (uint)2;

            _triggeredUnit1 = new MossUnit(
                this,
                _tradingFeed,
                _settings,
                !order.IsBuy,
                (double)order.Price,
                _movingAverage,
                quantity,
                _phase1RSI,
                _phase2RSI,
                _phase3RSI,
                _phase4RSI,
                _phase1Price,
                _phase3Price);

            _manager.AddStrategyUnit(_triggeredUnit1);
            _triggeredUnit1.RegisterEntry((double)order.PriceAverage, order.TimeUpdated);

            quantity = order.QuantityFilled - quantity;

            if (order.IsBuy)
            {
                _triggeredUnit2 = new MossUnit(
                    this,
                    _tradingFeed,
                    _settings,
                    false,
                    (double)order.Price,
                    _bollyHigh,
                    quantity,
                    _phase1RSI,
                    _phase2RSI,
                    _phase3RSI,
                    _phase4RSI,
                    _phase1Price,
                    _phase3Price);
            }
            else
            {
                _triggeredUnit2 = new MossUnit(
                    this,
                    _tradingFeed,
                    _settings,
                    true,
                    (double)order.Price,
                    _bollyLow,
                    quantity,
                    _phase1RSI,
                    _phase2RSI,
                    _phase3RSI,
                    _phase4RSI,
                    _phase1Price,
                    _phase3Price);
            }

            _manager.AddStrategyUnit(_triggeredUnit2);
            _triggeredUnit2.RegisterEntry((double)order.PriceAverage, order.TimeUpdated);
        }

        public void OnSnatchRejected(LimitSnatcher.LimitSnatchOrder snatcher, Objects.Finance.Orders.Order order)
        {
            System.Diagnostics.Debug.Assert(snatcher == _entryOrder);
            Phase = Phases.BuildingData;
        }

        #endregion

        #endregion
        //////////
        #region Enums

        public enum Phases
        {
            Initialised,
            BuildingData,
            Analysing,
            Phase1,
            Phase2,
            Phase3,
            Phase4,
            Triggered,
            FillFailure,
            DoneForTheDay
        }

        #endregion

    }
}